package mviewer.model3dLoader;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import mviewer.types.Material;
import mviewer.types.VectorData;

public class MtlLibLoader {

	private Map<String, ObjModelLoader.ITokenParser> parser;
	private Map<String, Material> materials;
	private Material current;
	private String workingDirectory;

	public MtlLibLoader() {
		parser = new HashMap<String, ObjModelLoader.ITokenParser>();
		UnsupportedFeatureParser ufp = new UnsupportedFeatureParser();
		TransparencyParser tp = new TransparencyParser();

		parser.put("d",			tp);
		parser.put("Tr",		tp);
		parser.put("newmtl",	new NewMtlParser());
		parser.put("Ka",		new AmbientColorParser());
		parser.put("Kd",		new DiffuseColorParser());
		parser.put("Ks",		new SpecularColorParser());
		parser.put("Ns",		new ShininessParser());
		parser.put("Tf",		ufp);
		parser.put("Ni",		ufp);
		parser.put("illum",		ufp);
		parser.put("map_Ka",	ufp);
		parser.put("map_Kd",	new TextureParser());
		parser.put("map_Ks",	ufp);
		parser.put("map_d",		ufp);
		parser.put("map_bump",	ufp);
		parser.put("bump",		ufp);
	}
	
	public Map<String, Material> load(String file)
			throws FileNotFoundException, IOException {
		int lastSeparator = file.lastIndexOf(File.separator) + 1;
		workingDirectory = file.substring(0, lastSeparator);
		materials = new HashMap<String, Material>();
		current = null;
		BufferedReader reader = new BufferedReader(new FileReader(file));
		String line;
		String[] token;
		String[] dataToken;
		while (reader.ready()) {
			line = reader.readLine();
			if (!line.trim().isEmpty()) {
				token = line.split(" ");
				if (!token[0].equals("#")) {
					dataToken = Arrays.copyOfRange(token, 1, token.length);
					try {
						parser.get(token[0]).parse(dataToken);
					} catch(NullPointerException e) {
						throw new IllegalArgumentException("Unknown MTL file element: " + token[0], e);
					}
				}
			}
		}
		reader.close();
		return materials;
	}

	private class UnsupportedFeatureParser implements ObjModelLoader.ITokenParser {
		public void parse(String[] token) throws IOException { }
	}

	private class NewMtlParser implements ObjModelLoader.ITokenParser {
		public void parse(String[] token) throws IOException {
			current = new Material(token[0]);
			materials.put(current.getName(), current);
		}
	}

	private class TransparencyParser implements ObjModelLoader.ITokenParser {
		public void parse(String[] token) throws IOException {
			try {
				current.setOpacity(Float.valueOf(token[0]));
			} catch(NumberFormatException e) {
				throw new IllegalArgumentException("Word found but opaque value (0..1) expected: " + Arrays.toString(token));
			}
		}
	}

	private class AmbientColorParser implements ObjModelLoader.ITokenParser {
		public void parse(String[] token) throws IOException {
			try {
				VectorData color = new VectorData(Float.valueOf(token[0]),
												Float.valueOf(token[1]),
												Float.valueOf(token[2]));
				current.setAmbientColor(color);
			} catch(NumberFormatException e) {
				throw new IllegalArgumentException("Word found but normalized color value (0..1) expected: " + Arrays.toString(token));
			}
		}
	}

	private class DiffuseColorParser implements ObjModelLoader.ITokenParser {
		public void parse(String[] token) throws IOException {
			try {
				VectorData color = new VectorData(Float.valueOf(token[0]),
												Float.valueOf(token[1]),
												Float.valueOf(token[2]));
				current.setDiffuseColor(color);
			} catch(NumberFormatException e) {
				throw new IllegalArgumentException("Word found but normalized color value (0..1) expected: " + Arrays.toString(token));
			}
		}
	}

	private class SpecularColorParser implements ObjModelLoader.ITokenParser {
		public void parse(String[] token) throws IOException {
			try {
				VectorData color = new VectorData(Float.valueOf(token[0]),
												Float.valueOf(token[1]),
												Float.valueOf(token[2]));
				current.setSpecularColor(color);
			} catch(NumberFormatException e) {
				throw new IllegalArgumentException("Word found but normalized color value (0..1) expected: " + Arrays.toString(token));
			}
		}
	}

	private class ShininessParser implements ObjModelLoader.ITokenParser {
		public void parse(String[] token) throws IOException {
			try {
				current.setShininess(Float.valueOf(token[0]));
			} catch(NumberFormatException e) {
				throw new IllegalArgumentException("Word found but shininess factor (0..1000) expected: " + Arrays.toString(token));
			}
		}
	}

	private class TextureParser implements ObjModelLoader.ITokenParser {
		public void parse(String[] token) throws IOException {
			String texFile = token[0].charAt(0) == File.separatorChar ? token[0]
								: workingDirectory + token[0];
			current.setTextureFile(texFile);
		}
	}
}